// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.Serialization; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Interfaces; namespace LargoCommon.Music { /// Rhythmical structure. /// Rhythmical structure represents rhythm of one bar. It is always assigned to certain /// rhythmical modality (and to rhythmical system). It has some inner characteristics /// (mobility, tension, entropy,..). [Serializable] [XmlRoot] public sealed class RhythmicStructure : FiguralSchema, IRhythmic, IModalStruct { #region Constructors /// Initializes a new instance of the RhythmicStructure class. Serializable. public RhythmicStructure() { } /// /// Initializes a new instance of the RhythmicStructure class. /// /// The given system. /// Structural code. public RhythmicStructure(GeneralSystem givenSystem, string structuralCode) : base(givenSystem, structuralCode) { Contract.Requires(givenSystem != null); this.DetermineToneLevel(); } /// /// Initializes a new instance of the RhythmicStructure class. /// /// The given system. /// Number of instance. public RhythmicStructure(GeneralSystem givenSystem, decimal number) : base(givenSystem, number) { Contract.Requires(givenSystem != null); this.DetermineToneLevel(); } /// Initializes a new instance of the RhythmicStructure class. /// Rhythmical structure. public RhythmicStructure(FiguralStructure structure) : base(structure) { Contract.Requires(structure != null); this.DetermineToneLevel(); } /// /// Initializes a new instance of the RhythmicStructure class. /// /// System order. /// Binary Shape. public RhythmicStructure(byte sysOrder, RhythmicShape shape) //// : base(RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, sysOrder), null) { Contract.Requires(shape != null); if (shape == null || this.GSystem.Order == 0) { return; } this.SetElement(0, 2); //// start of pause needed! for (byte e = 0; e < this.GSystem.Order; e++) { if (shape.IsOn(e)) { this.SetElement(e, (byte)RhythmicElement.StartTone); } } //// 2016/05 this.CompleteFromElements(); this.DetermineBehavior(); } /// /// Initializes a new instance of the RhythmicStructure class. Serializable. /// /// Rhythmical order. /// Musical tones. public RhythmicStructure(byte rhythmicOrder, IList musicalTones) : base(RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder), null) { Contract.Requires(musicalTones != null); //// if (musicalTones == null) { return; } var cnt = musicalTones.Count; this.SetElement(0, (byte)RhythmicElement.StartRest); //// Potential pause start for (var i = 0; i < cnt; i++) { var mt = musicalTones.ElementAt(i); if (mt == null) { continue; } //// Tone start this.SetElement(mt.BitFrom, mt.IsPause ? (byte)RhythmicElement.StartRest : (byte)RhythmicElement.StartTone); //// Potential pause start var limitTo = rhythmicOrder; if (i + 1 < cnt) { var nextTone = musicalTones.ElementAt(i + 1); if (nextTone != null) { limitTo = nextTone.BitFrom; } } if (mt.BitRange.BitTo + 1 < limitTo) { //// (mt.BarNumberFrom) this.SetElement((byte)(mt.BitRange.BitTo + 1), (byte)RhythmicElement.StartRest); //// (mt.BarNumberFrom) } } //// 2015/01 Added condition && !firstTone.IsPause if (musicalTones.FirstOrDefault() is MusicalStrike firstTone && firstTone.BitFrom == 0 && !firstTone.IsPause && firstTone.IsFromPreviousBar) { this.SetElement(0, (byte)RhythmicElement.ContinuePrevious); } //// this.DetermineINumber(); this.DetermineLevel(); this.DetermineToneLevel(); //// this.ResetSchema(); //// this.ComputeRhythmicProperties(); } /// Initializes a new instance of the RhythmicStructure class. /// The given system. /// The mark rhythm. public RhythmicStructure(GeneralSystem givenSystem, XElement markRhythm) : base(givenSystem, string.Empty) { string code = XmlSupport.ReadStringAttribute(markRhythm.Attribute("Code")); this.SetStructuralCode(code); } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public XElement GetXElement { get { var xe = new XElement( "Structure", new XAttribute("Code", this.GetStructuralCode), new XAttribute("Schema", this.ElementSchema)); return xe; } } #endregion #region Interface - simple properties /// Gets or sets Tone Level. /// Property description. public byte ToneLevel { get; set; } /// /// Gets a value indicating whether is from previous bar. /// /// The is from previous bar. public bool IsFromPreviousBar => this.ElementList.ElementAt(0) == (byte)RhythmicElement.ContinuePrevious; /// /// Gets a value indicating whether this instance has properties. /// /// /// Is true if this instance has properties; otherwise, false. /// public bool HasProperties => this.Properties.ContainsKey(GenProperty.FormalMobility); /// /// Gets or sets a value indicating whether this instance is used. /// /// /// true if this instance is used; otherwise, false. /// public bool IsUsed { get; set; } /// /// Gets the element schema and occurrence. /// /// Property description. [UsedImplicitly] public string ElementSchemaAndOccurrence => string.Format( CultureInfo.CurrentCulture, "({0}/{1}) {2} ({3})", this.ToneLevel, this.Level, this.ElementSchema, this.Occurrence); /// /// Gets a value indicating whether [start with formal rest]. /// /// /// True if [start with formal rest]; otherwise, false. /// public bool StartsWithFormalRest { get { if (this.ElementList.Count == 0) { return true; } var first = (byte)this.ElementList[0]; if (first != 2) { return false; } byte length = 1; for (byte i = 1; i < this.ElementList.Count; i++) { var value = (byte)this.ElementList[i]; length++; if (value != (byte)RhythmicElement.ContinuePrevious) { break; } } return length >= this.RhythmicSystem.Order / 3; } } /// /// Gets a value indicating whether [end with formal rest]. /// /// /// true if [end with formal rest]; otherwise, false. /// public bool EndsWithFormalRest { get { byte length = 0; for (var i = this.ElementList.Count - 1; i >= 0; i--) { var value = (byte)this.ElementList[i]; if (value == (byte)RhythmicElement.StartTone) { return false; } length++; if (value == (byte)RhythmicElement.StartRest) { break; } } return length >= this.RhythmicSystem.Order / 3; } } #endregion #region Interface - object properties /// Gets rhythmical system. /// Property description. [XmlIgnore] public RhythmicSystem RhythmicSystem => (RhythmicSystem)this.GSystem; /// Gets or sets rhythmical modality. /// Property description. [XmlIgnore] public RhythmicModality RhythmicModality { get => (RhythmicModality)this.Modality; set => this.Modality = value; } /// /// Gets the get rhythmic shape. /// /// /// The get rhythmic shape. /// public RhythmicShape GetRhythmicShape { get { var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Shape, this.Order); var bitArray = new BitArray(this.Order); for (byte i = 0; i < this.Order; i++) { bitArray[i] = this.IsOn(i); } var shape = new RhythmicShape(rsystem, bitArray); return shape; } } /// Gets or sets previous rhythmical structure. /// Property description. [XmlIgnore] public RhythmicStructure PreviousStruct { get; set; } /// /// Gets the code mark. /// /// /// The code mark. /// public string CodeMark { get { int beat = (int)this.RhythmicBehavior.Beat / 4; //// ?!?!? bounds of Variance??? RhythmicBehavior.Tension var beatCode = MusicalProperties.GetLetter(beat, true); int tension = (int)this.FormalBehavior.Variance / 10; //// ?!?!? bounds of Variance??? RhythmicBehavior.Tension var tensionCode = MusicalProperties.GetLetter(tension, true); var codeMark = string.Format( "{0,2}/{1,2}-{2}.{3}", this.Level, this.Level - this.ToneLevel, tensionCode, beatCode); return codeMark; } } #endregion #region Static factory methods /// /// Get new harmonic structure. /// /// The given system. /// Structural number. /// /// Returns value. /// public static RhythmicStructure GetNewRhythmicStructure(GeneralSystem givenSystem, long number) { Contract.Requires(givenSystem != null); Contract.Ensures(Contract.Result() != null); var rs = new RhythmicStructure(givenSystem, number); rs.DetermineBehavior(); return rs; } /// /// Gets Rhythmic Structure. /// /// The given system. /// Structural code. /// /// Returns value. /// public static RhythmicStructure GetNewRhythmicStructure(GeneralSystem givenSystem, string structuralCode) { Contract.Requires(givenSystem != null); Contract.Ensures(Contract.Result() != null); var rs = new RhythmicStructure(givenSystem, structuralCode); rs.DetermineBehavior(); return rs; } /// /// Gets the full structure. /// /// The rhythmic order. /// Returns value. public static RhythmicStructure GetFullStructure(byte rhythmicOrder) { var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder); var code = $"1,{rhythmicOrder - 1}*0"; var rstruct = GetNewRhythmicStructure(rsystem, code); return rstruct; } /// /// Gets the rest structure. /// /// The rhythmic order. /// Returns value. public static RhythmicStructure GetRestStructure(byte rhythmicOrder) { var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder); var code = $"2,{rhythmicOrder - 1}*0"; var rstruct = GetNewRhythmicStructure(rsystem, code); return rstruct; } #endregion #region Public methods /// Makes a deep copy of the RhythmicStructure object. /// Returns object. public override object Clone() { return GetNewRhythmicStructure(this.GSystem, this.GetStructuralCode); } /// /// Range of bit pair on given Level. /// /// /// Returns value. /// public BitRange OverrunRange() { if (this.IsOn(0)) { //// this.Level == 0 || return null; } var order = this.GSystem.Order; byte place = 0; //// this.PlaceAtLevel(0); for (byte tick = 0; tick < order; tick++) { if (tick > this.ElementList.Count - 1) { //// 2014/01 break; } if (this.ElementList[tick] <= 0) { continue; } place = tick; break; } if (place == 0) { place = order; } var range = new BitRange(order, 0, place); return range; } /// /// Half-divided structure. /// /// /// Returns value. /// public RhythmicStructure HalfDividedStructure() { var rs = new RhythmicStructure { GSystem = this.GSystem }; byte cnt = 0; short lastToneElement = -1; byte lastValue = 0; for (byte i = 0; i < this.ElementList.Count; i++) { var value = (byte)this.ElementList[i]; if (value > 0) { if (value == 1) { if (lastToneElement >= 0) { AddPartialElements(rs, cnt, lastValue); } lastValue = value; } lastToneElement = i; cnt = 0; } cnt++; } if (lastToneElement >= 0) { AddPartialElements(rs, cnt, lastValue); } rs.CompleteFromElements(); rs.DetermineBehavior(); return rs; } /// /// Inverted structure. /// /// Returns value. public new RhythmicStructure InvertedStructure() { var rs = new RhythmicStructure { GSystem = this.GSystem }; byte cnt = 0; for (byte i = 0; i < this.ElementList.Count; i++) { var value = (byte)this.ElementList[this.ElementList.Count - i - 1]; if (value > 0) { rs.ElementList.Add(value); for (byte n = 0; n < cnt; n++) { rs.ElementList.Add(0); } cnt = 0; } else { cnt++; } } rs.CompleteFromElements(); rs.DetermineBehavior(); return rs; } /// Validity test. /// Returns value. public override bool IsEmptyStruct() { return this.IsOff(0) || base.IsEmptyStruct(); } /// Validity test. /// Returns value. public override bool IsValidStruct() { var ok = true; byte lastNum = 0; for (byte e = 0; e < this.GSystem.Order; e++) { if (e >= this.ElementList.Count) { continue; } var num = (byte)this.ElementList[e]; if (num == (byte)RhythmicElement.StartRest && lastNum == (byte)RhythmicElement.StartRest) { // two pauses in sequence ok = false; break; } if (num != 0) { lastNum = num; } } return ok; } /// Evaluate properties of the structure. public override void DetermineBehavior() { //// this.SetElements(); this.DetermineToneLevel(); this.ComputeRhythmicProperties(); this.ComputeMobility(); } /// /// Converts to system. /// /// The given system. /// /// Returns value. /// public RhythmicStructure ConvertToSystem(GeneralSystem givenSystem) { //// RhythmicSystem Contract.Requires(givenSystem != null); var realRhythmicOrder = givenSystem.Order; var d = (float)realRhythmicOrder / this.Order; var realStruct = new RhythmicStructure(givenSystem, string.Empty); float index = 0; for (byte bit = 0; index < realRhythmicOrder; index += d, bit++) { var element = (byte)Math.Round(index); if (bit < this.ElementList.Count && element < realStruct.ElementList.Count) { realStruct.ElementList[element] = this.ElementList[bit]; } } //// 2016/05 realStruct.CompleteFromElements(); realStruct.DetermineBehavior(); return realStruct; } /// /// Determine ToneLevel. /// public void DetermineToneLevel() { byte s = 0; for (byte e = 0; e < this.GSystem.Order; e++) { if (e < this.ElementList.Count && this.ElementList[e] == 1) { s++; } } this.ToneLevel = s; if (!this.Properties.ContainsKey(GenProperty.ToneLevel)) { this.Properties[GenProperty.ToneLevel] = this.ToneLevel; } } #endregion #region Comparison /// Support sorting according to level and ElementSchema. /// Object to be compared. /// Returns value. public override int CompareTo(object obj) { if (!(obj is RhythmicStructure rs)) { return 0; } if (this.Level < rs.Level) { return -1; } return this.Level > rs.Level ? 1 : string.Compare(this.ElementSchema, rs.ElementSchema, StringComparison.Ordinal); //// This kills the DataGrid //// throw new ArgumentException("Object is not a RhythmicStructure"); } /// Test of equality. /// Object to be compared. /// Returns value. public override bool Equals(object obj) { //// check null (this pointer is never null in C# methods) if (object.ReferenceEquals(obj, null)) { return false; } if (object.ReferenceEquals(this, obj)) { return true; } if (this.GetType() != obj.GetType()) { return false; } return this.CompareTo(obj) == 0; } /// Support of comparison. /// Returns value. public override int GetHashCode() { return this.ElementSchema.GetHashCode(); } #endregion #region String representation /// List of figure elements. /// Returns value. public override string ElementString() { var s = new StringBuilder(); var median = this.GSystem.Order / 2; for (byte e = 0; e < median; e += 1) { //// 2016/04 was e += 2 if ((e * 2) + 1 >= this.ElementList.Count) { continue; } var elem1 = (byte)this.ElementList[e * 2]; var elem2 = (byte)this.ElementList[(e * 2) + 1]; if (elem2 == 0) { switch (elem1) { case 0: s.Append("-"); break; case 1: s.Append("T"); break; case 2: s.Append("P"); break; //// resharper default: break; } } else { s.Append("X"); } } return s.ToString(); } /// Short string representation of the object. /// Returns value. public string ToShortString() { var s = new StringBuilder(); s.Append(" " + this.ElementString()); //// s.Append(" " + this.DistanceString()); return s.ToString(); } /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.Append(string.Format(CultureInfo.InvariantCulture, "{0},{1}", base.ToString(), this.ElementString())); s.Append(","); s.AppendLine(this.StringOfProperties()); return s.ToString(); } #endregion #region Relation to previous struct /// Sets previous harmonic structure and Compute corresponding properties. /// Harmonic structure. public void SetPreviousStruct(RhythmicStructure structure) { Contract.Requires(structure != null); this.PreviousStruct = structure; this.DetermineBehaviorFromPreviousStruct(); } #endregion #region Private static methods /// /// Adds the partial elements. /// /// The rhythmic structure. /// The CNT. /// The last value. private static void AddPartialElements(RhythmicStructure rs, byte cnt, byte lastValue) { Contract.Requires(rs != null); var d = (byte)(cnt / 2); rs.ElementList.Add(lastValue); for (byte n = 0; n < d; n++) { rs.ElementList.Add(0); } if (lastValue == 1 && cnt % 2 == 0) { rs.ElementList.Add(lastValue); } for (byte n = 0; n < d; n++) { rs.ElementList.Add(0); } } #endregion #region Private methods /// /// Determine and sets the mobility property. /// /// Returns value. private float ComputeToneMobility() { //// float mobility = this.GSystem.Order > 0 ? (this.Level / (float)this.GSystem.Order) * 100f : 0; //// float mobility = this.GSystem.Order > 0 ? (this.ToneLevel / (float)this.GSystem.Order) * 100f : 0; var toneMobility = (this.ToneLevel / (float)this.Level) * 100f; return toneMobility; } /// Sets properties of the structure with regard to previous structure. private void DetermineBehaviorFromPreviousStruct() { Contract.Requires(this.PreviousStruct != null); //// var rhythmicSystem = (RhythmicSystem)this.GSystem; //// var harRelation = new HarmonicRelation(harmonicSystem, this.PreviousStruct, this); //// var continuity = harRelation.MeanValueOfProperty(GenProperty.FormalContinuity, false, true); //// var impulse = harRelation.MeanValueOfProperty(GenProperty.FormalImpulse, false, true); //// this.Properties[GenProperty.RelatedContinuity] = continuity; // add with repetition //// this.Properties[GenProperty.RelatedImpulse] = impulse; // add with repetition } #endregion } }